package com.testing.httpclient;


import com.testing.utils.LoggerConfig;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpClientKeyWord {

    // 保存参数
    private Map<String,String> parMap = new HashMap<String, String>();

    // 设置socket超时时间
    private final static int SocketTimeout = 15000;

    // 设置请求响应时间
    private final static int ConnectTimeout = 10000;

    // 保存body信息
    private String body;
    // 是否使用cookie标志位，默认使用cookie
    private boolean useCookie = true;

    // cookieStore对象，httpclient用它来记录得到的cookie值
    private CookieStore cookieStore = new BasicCookieStore();

    // 设置是什么浏览器访问
    private final String UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36";

    // 头部信息，主要用来确定表单提交方式
    private final String[] ContentType = {"application/x-www-form-urlencoded; charset=UTF-8", "application/json; charset=UTF-8", "multiPart/form-data; charset=UTF-8"};

    private final String DECODE = "UTF-8";

    // 设置是否添加header
    private boolean addHeader = false;

    // 匹配unicode编码格式的正则表达式。
    private static final Pattern reUnicode = Pattern.compile("\\\\u([0-9a-zA-Z]{4})");

    // 用于保存上个请求保存的数据
    private Map<String, String> headerMap = new HashMap<String, String>();

    private CloseableHttpClient httpClient;

    // 保存参数
    public void addParameter(String key,String value){
        parMap.put(key,value);
    }

    /**
     * 通过saveCookie来使用成员变量的cookieStore 通过一个成员变量标志位useCookie来进行判断
     */
    public void saveCookie() {
        // 设置useCookie的状态为真
        useCookie = true;
    }

    /**
     * 清理掉成员变量cookieStore池中所有的cookie
     * 并且通过指定成员变量标志位useCookie为false，不让创建client调用已有的cookieStore
     */
    public void clearCookie() {
        useCookie = false;
        cookieStore = new BasicCookieStore();
    }

    /**
     * 清除header
     */
    public void clearHeader() {
        addHeader = false;
        headerMap.clear();
    }

    /**
     * 添加header
     * @param par 传入map
     */
    public void addHeader(Map<String, String> par) {
        addHeader = true;
        if (par.size() > 0) {
            for (String str : par.keySet()) {
                headerMap.put(str, par.get(str));
            }
        }
    }

    /**
     * 查找字符串中的unicode编码并转换为中文。
     * @param u 传入字符串
     * @return 返回中文
     */
    private static String DeCode(String u) {
        try {
            Matcher m = reUnicode.matcher(u);
            StringBuffer sb = new StringBuffer(u.length());
            while (m.find()) {
                m.appendReplacement(sb, Character.toString((char) Integer.parseInt(m.group(1), 16)));
            }
            m.appendTail(sb);
            return sb.toString();
        } catch (Exception e) {
            LoggerConfig.LOGGER.error("转换中文失败");
            e.printStackTrace();
            return u;
        }
    }

    /**
     * SSLContext用于绕过ssl验证，使发包的方法能够对https的接口进行请求。
     */
    private static SSLContext createIgnoreVerifySSL() {

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        SSLContext sc = null;
        try {
            sc = SSLContext.getInstance("SSLv3");
            sc.init(null, new TrustManager[] { trustManager }, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sc;
    }

    /**
     * @return 返回一个httpclient 对象
     */
    private CloseableHttpClient createClient() {
        // 采用绕过验证的方式处理https请求
        SSLContext sslcontext = createIgnoreVerifySSL();
        // 设置协议http和https对应的处理socket链接工厂的对象
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        // 创建自定义的httpclient对象
        CloseableHttpClient client;
        // 基于useCookie标志位进行判断，如果为真则创建client的时候，使用cookieStore
        if (useCookie) {
            client = HttpClients.custom().setConnectionManager(connManager).setDefaultCookieStore(cookieStore).build();
        } else {
            // 如果为假创建clients的时候，不使用cookieStore
            client = HttpClients.custom().setConnectionManager(connManager).build();
        }
        // 当需要进行代理抓包时，启用下面的代码。
        // 设置代理地址，适用于需要用fiddler抓包时使用，不用时切记注释掉这句！
//		HttpHost proxy = new HttpHost("localhost", 8888, "http");
//		if (useCookie) {
//			client = HttpClients.custom().setProxy(proxy).setConnectionManager(connManager)
//					.setDefaultCookieStore(cookieStore).build();
//		} else {
//			client = HttpClients.custom().setProxy(proxy).setConnectionManager(connManager).build();
//		}
        return client;
    }

    /**
     * @param url 传入url地址
     * @param par 传入拼接参数
     */
    public String doGet(String url, String par) {
        httpClient = createClient();
        if (par.replace(" ","").trim().length() > 0) {
            url = url + "?" + par;
        }
        LoggerConfig.LOGGER.info("请求地址为:" + url);
        HttpGet httpGet = new HttpGet(url);
        // 设置连接的超时时间
        // setSocketTimeout指定收发包过程中的超时上线是15秒，connectTime指定和服务器建立连接，还没有发包时的超时上限为10秒。
        RequestConfig config = RequestConfig.custom().setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout).build();
        httpGet.setConfig(config);
        httpGet.setHeader("User-Agent", UserAgent);
        httpGet.setHeader("Content-Type", ContentType[0]);
        if (addHeader) {
            for (String header : headerMap.keySet()) {
                httpGet.addHeader(header, headerMap.get(header));
            }
        }
        try {
            LoggerConfig.LOGGER.info("请求地址为:" + httpGet.getURI());
            CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                body = EntityUtils.toString(httpEntity, DECODE);
                body = DeCode(body); // 转为中文
                EntityUtils.consume(httpEntity); // 释放返回实体
                LoggerConfig.LOGGER.info("接口返回结果为:" + body);
            }
        } catch (IOException e) {
            body = e.fillInStackTrace().toString();
            e.printStackTrace();
        }
        return body;
    }

    /**
     * 通过form表单提交
     * @param url 请求地址
     * @param map 请求参数
     */
    public String doPostForm(String url, Map<String, String> map) {
        httpClient = createClient();
        HttpPost httpPost = new HttpPost(url);
        // 设置连接的超时时间
        // setSocketTimeout指定收发包过程中的超时上线是15秒，connectTime指定和服务器建立连接，还没有发包时的超时上限为10秒。
        RequestConfig config = RequestConfig.custom().setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout).build();
        httpPost.setConfig(config);
        httpPost.setHeader("User-Agent", UserAgent);
        httpPost.setHeader("Content-Type", ContentType[0]);
        // 判断是否需要添加header信息
        if (addHeader) {
            for (String header : headerMap.keySet()) {
                httpPost.addHeader(header, headerMap.get(header));
            }
        }
        List<NameValuePair> list = new ArrayList<NameValuePair>();
        // 当前map中存在值时，form表单发送
        if (map.size() > 0) {
            for (String key : map.keySet()) {
                list.add(new BasicNameValuePair(key, map.get(key)));
            }
        }
        LoggerConfig.LOGGER.info("请求地址为:" + httpPost.getURI());
        try {
            UrlEncodedFormEntity entry = new UrlEncodedFormEntity(list, DECODE);
            httpPost.setEntity(entry);
            try {
                CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();
                if (httpEntity != null) {
                    body = EntityUtils.toString(httpEntity, DECODE);
                    EntityUtils.consume(httpEntity);
                    body = DeCode(body);
                    LoggerConfig.LOGGER.info("接口返回结果为:" + body);
                }
                httpResponse.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (UnsupportedEncodingException e) {
            body = e.fillInStackTrace().toString();
            e.printStackTrace();
        }finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    /**
     * @param url  传入url地址
     * @param json 传入json字符串
     */
    public String doPostJson(String url, String json) {
        httpClient = createClient();
        HttpPost httpPost = new HttpPost(url);
        // 设置连接的超时时间
        // setSocketTimeout指定收发包过程中的超时上线是15秒，connectTime指定和服务器建立连接，还没有发包时的超时上限为10秒。
        RequestConfig config = RequestConfig.custom().setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout).build();
        httpPost.setConfig(config);
        httpPost.setHeader("User-Agent", UserAgent);
        httpPost.setHeader("Content-Type", ContentType[1]);
        // 判断是否需要添加header信息
        if (addHeader) {
            for (String header : headerMap.keySet()) {
                httpPost.addHeader(header, headerMap.get(header));
            }
        }
        try {
            StringEntity jsonPar = new StringEntity(json);
            jsonPar.setContentType("application/json");
            jsonPar.setContentEncoding(DECODE);
            httpPost.setEntity(jsonPar);
            try {
                LoggerConfig.LOGGER.info("请求地址为:" + httpPost.getURI());
                CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();
                if (httpEntity != null) {
                    body = EntityUtils.toString(httpEntity, DECODE);
                    EntityUtils.consume(httpEntity);
                    body = DeCode(body);
                    LoggerConfig.LOGGER.info("接口返回结果为:" + body);
                }
                httpResponse.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (UnsupportedEncodingException e) {
            body = e.fillInStackTrace().toString();
            e.printStackTrace();
        }finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    /**
     * @param url 传入url地址
     * @param map 传入map
     */
    private String doPostMultiPart(String url, Map<String, String> map) {
        httpClient = createClient();
        HttpPost httpPost = new HttpPost(url);
        // 设置连接的超时时间
        // setSocketTimeout指定收发包过程中的超时上线是15秒，connectTime指定和服务器建立连接，还没有发包时的超时上限为10秒。
        RequestConfig config = RequestConfig.custom().setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout).build();
        httpPost.setConfig(config);
        httpPost.setHeader("User-Agent", UserAgent);
        // 判断是否需要添加header信息
        if (addHeader) {
            for (String header : headerMap.keySet()) {
                httpPost.addHeader(header, headerMap.get(header));
            }
        }
        // Multipart发包
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        if (map.size() > 0) {
            for (String str : map.keySet()) {
                multipartEntityBuilder.addTextBody(str, map.get(str));
            }
        }
        HttpEntity multipart = multipartEntityBuilder.build();
        // 设置参数
        httpPost.setEntity(multipart);
        try {
            LoggerConfig.LOGGER.info("请求地址为:" + httpPost.getURI());
            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                body = EntityUtils.toString(httpEntity, DECODE);
                LoggerConfig.LOGGER.info("接口返回结果为:" + body);
            }
            httpResponse.close();
        } catch (IOException e) {
            e.printStackTrace();
            body = e.fillInStackTrace().toString();
        }finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    /**
     * @param text 传入字符串
     * @return 返回替换后的字符串
     */
    public String parReplace(String text){
        if (text.length() > 0){
            for (String str: parMap.keySet()) {
                text = text.replaceAll("\\{"+ str + "}" , parMap.get(str));
            }
        }
        return text;
    }
}
